﻿using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace UnityTouchTable.Runtime
{
    /// <summary>
    /// 角度旋转器
    /// </summary>
    [AddComponentMenu("TouchTable/AngleRotate", 4)]
    [DisallowMultipleComponent]
    public class AngleRotate : MonoBehaviour, IAnglehandle
    {
        #region EventHandler
        /// <summary>
        /// 当旋转方向改变
        /// </summary>
        private EventHandler m_OnChangeSenseRotation;
        #endregion

        #region Event
        /// <summary>
        /// 旋转方向改变
        /// </summary>
        public event EventHandler ChangeSenseRotation
        {
            add => this.m_OnChangeSenseRotation += value;
            remove => this.m_OnChangeSenseRotation -= value;
        }
        #endregion

        #region Field
        /// <summary>
        /// 当移动时是否更新角度
        /// </summary>
        [SerializeField]
        [Header("当移动时是否更新角度")]
        private bool m_OnMoveUpdateAngle = false;

        /// <summary>
        /// 旋转方向
        /// </summary>
        private SenseRotation m_SenseRotation = SenseRotation.Stationary;

        /// <summary>
        /// 下一个旋转方向
        /// </summary>
        private SenseRotation m_NextSenseRotation = SenseRotation.Stationary;

        /// <summary>
        /// 上一次角度
        /// </summary>
        private float m_LastAngle = default;

        /// <summary>
        /// 扰动角度
        /// </summary>
        [SerializeField]
        [Header("扰动角度")]
        [Range(1.0f, 10.0f)]
        private float m_DisturbanceAngle = 3.0f;

        /// <summary>
        /// 角度停留时长
        /// </summary>
        [SerializeField]
        [Header("角度停留时长")]
        [Range(0.1f, 1.0f)]
        private float m_DirStayTime = 0.25f;

        /// <summary>
        /// 能否计时
        /// </summary>
        private bool m_CanTimer = false;

        /// <summary>
        /// 计时器
        /// </summary>
        private float m_Timer = 0.0f;
        #endregion

        #region Property
        /// <summary>
        /// 获取当移动时是否更新角度
        /// </summary>
        public bool OnMoveUpdateAngle => this.m_OnMoveUpdateAngle;
        #endregion

        private void Update()
        {
            if (!this.m_CanTimer)
                return;

            this.m_Timer += Time.deltaTime;
            if (this.m_Timer >= this.m_DirStayTime)
            {
                this.m_SenseRotation = this.m_NextSenseRotation;
                this.m_CanTimer = false;
                this.m_Timer = 0.0f;

                this.m_OnChangeSenseRotation?.Invoke(this, EventArgs.Empty);
            }
        }


        #region Function
        /// <summary>
        /// 更新角度
        /// </summary>
        /// <param name="angle">角度</param>
        public void UpdateAngle(float angle)
        {
            float differenceAngle = angle - this.m_LastAngle;
            float deltaAngle = Mathf.Abs(differenceAngle);
            if (deltaAngle < this.m_DisturbanceAngle)
            {
                this.m_LastAngle = angle;
                return;
            }

            float sumAngle = angle + this.m_LastAngle;
            float ratio = deltaAngle / sumAngle;

            bool crossDomain = ratio > 0.95f;
            float sign = Mathf.Sign(differenceAngle);

            //预测旋转方向
            SenseRotation forecast = SenseRotation.Stationary;
            //负
            if (sign < 0)
                forecast = crossDomain ? SenseRotation.Clockwise : SenseRotation.Anticlockwise;
            //0
            else if (sign == 0)
            {
                //do nothing
            }
            //正
            else
                forecast = crossDomain ? SenseRotation.Anticlockwise : SenseRotation.Clockwise;

            this.m_LastAngle = angle;
            this.m_Timer = 0.0f;
            if (this.m_NextSenseRotation == forecast)
                this.m_CanTimer = false;
            else
            {
                this.m_CanTimer = true;
                this.m_NextSenseRotation = forecast;
            }
        }
    }
    #endregion
}